﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml;
using DiscoveryLogic.Analytics.DataObjects;
using DiscoveryLogic.Analytics.DataObjects.Rules;
using DiscoveryLogic.Analytics.DataObjects.Visuals;
using DiscoveryLogic.Common.Utilities;
using DiscoveryLogic.Common.Utilities.OpenXml;
using Microsoft.Windows.Controls;
using MessageBox=System.Windows.Forms.MessageBox;
using OpenFileDialog=Microsoft.Win32.OpenFileDialog;
using TableColumn=DiscoveryLogic.Common.Utilities.TableColumn;
using UserControl=System.Windows.Controls.UserControl;

namespace DiscoveryLogic.Analytics.Modules.ImportRules
{
    /// <summary>
    /// Interaction logic for ImportRuleEditor.xaml
    /// </summary>
    public partial class ImportRuleEditor : UserControl
    {
        #region events
        public delegate void OnCancel();
        public event OnCancel Cancel;

        public delegate void OnImportRuleDefined(EntityImportRule rule);
        public event OnImportRuleDefined ImportRuleDefined;

        #endregion

        #region fields
        private EntityImportRule _ImportRule;
        #endregion

        public ImportRuleEditor()
        {
            InitializeComponent();
            this._ImportRule=new EntityImportRule(
                "New Import Rule", SetDataSourceType.ExcelFile,
                EntityType.Grants, EntityIcons.ImportRuleIcon);
        }

        public ImportRuleEditor(EntityImportRule importRule)
        {
            InitializeComponent();
            this._ImportRule = importRule;
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            this.cboEntityType.Items.Clear();
            foreach(EntityType entityType in Enum.GetValues(typeof(EntityType)))
            {
                this.cboEntityType.Items.Add(entityType.ToString());
            }

            this.rdDB.IsChecked = true;
            if(this._ImportRule.SrcDataFormat==SetDataSourceType.ExcelFile)
                this.rdExcel.IsChecked = true;
            else if (this._ImportRule.SrcDataFormat == SetDataSourceType.XML)
                this.rdXml.IsChecked = true;
            this.txtRuleName.Text = this._ImportRule.RuleName;
            this.cboEntityType.Text = this._ImportRule.TgtEntityType.ToString();
            this.txtFilePath.Text = this._ImportRule.DataSourcePath;
            if(!string.IsNullOrEmpty(this.txtFilePath.Text) && File.Exists(this.txtFilePath.Text))
            {
                if (this.rdExcel.IsChecked.Value)
                {
                    List<string> sheetNames = ExcelReader.GetWorksheetNames(this.txtFilePath.Text);
                    this.PopulateWorksheets(sheetNames);
                }
                else if (this.rdXml.IsChecked.Value)
                {
                    this.UpdateXmlRecordPaths();
                }
                else if(this.rdDB.IsChecked.Value)
                {
                    string[] tblNames = DbUtil.GetTableNames(DataProviderType.MSSQL, string.Empty, this.txtFilePath.Text);
                    if (tblNames != null && tblNames.Length > 0)
                    {
                        foreach (string tblName in tblNames)
                        {
                            this.cboWorksheets.Items.Add(tblName);
                        }
                    }
                }
            }
            this.cboWorksheets.Text = this._ImportRule.RecordListPath;
            this.dvBindings.Rows.Clear();
            if(this._ImportRule.EntityFieldSrcFieldMappings !=null && 
                this._ImportRule.EntityFieldSrcFieldMappings.Count>0)
            {
                foreach(string propname in this._ImportRule.EntityFieldSrcFieldMappings.Keys)
                {
                    DataGridViewRow dr = new DataGridViewRow();
                    DataGridViewTextBoxCell cellProp=new DataGridViewTextBoxCell();
                    cellProp.Value = propname;
                    dr.Cells.Add(cellProp);
                    DataGridViewComboBoxCell cellSrcField=new DataGridViewComboBoxCell();
                    cellSrcField.Items.Add(this._ImportRule.EntityFieldSrcFieldMappings[propname]);
                    cellSrcField.Value = this._ImportRule.EntityFieldSrcFieldMappings[propname];
                    dr.Cells.Add(cellSrcField);
                    this.dvBindings.Rows.Add(dr);
                }
            }
        }

        private void btnBrowse_Click(object sender, RoutedEventArgs e)
        {
            if(this.rdExcel.IsChecked.Value || this.rdXml.IsChecked.Value)
            {
                OpenFileDialog dlg = new OpenFileDialog();
                if (this.rdExcel.IsChecked.Value)
                    dlg.Filter = "Excel files(*.xls;*.xlsx)|*.xls;*.xlsx|All files(*.*)|*.*";
                else if(this.rdXml.IsChecked.Value)
                    dlg.Filter = "Xml files(*.xml)|*.xml|All files(*.*)|*.*";
                bool? dialogResult = dlg.ShowDialog();
                if (dialogResult.HasValue && dialogResult.Value)
                {
                    this.txtFilePath.Text = dlg.FileName;
                    if (this.rdExcel.IsChecked.Value)
                    {
                        List<string> sheetNames = ExcelReader.GetWorksheetNames(dlg.FileName);
                        this.PopulateWorksheets(sheetNames);
                    }
                    else if(this.rdXml.IsChecked.Value)
                    {
                        this.UpdateXmlRecordPaths();
                    }
                }    
            }
            else if(this.rdDB.IsChecked.Value)
            {
                if (!string.IsNullOrEmpty(this.txtFilePath.Text))
                {
                    this.cboWorksheets.Items.Clear();
                    string[] tblNames = DbUtil.GetTableNames(DataProviderType.MSSQL, string.Empty, this.txtFilePath.Text);
                    if (tblNames != null && tblNames.Length > 0)
                    {
                        foreach (string tblName in tblNames)
                        {
                            this.cboWorksheets.Items.Add(tblName);
                        }
                    }
                }
            }
        }

        private void UpdateXmlRecordPaths()
        {
            if (this.rdXml.IsChecked.Value && !string.IsNullOrEmpty(this.txtFilePath.Text))
            {
                XmlTextReader xReader = new XmlTextReader(File.OpenRead(this.txtFilePath.Text));
                this.cboWorksheets.Items.Clear();
                List<string> allPaths = new List<string>();
                int lastDepth = -1;
                string lastNodeName = string.Empty;
                Stack<string> elementNames = new Stack<string>();
                while (xReader.Read())
                {
                    if (xReader.NodeType == XmlNodeType.Element)
                    {
                        int currentDepth = xReader.Depth;
                        if (currentDepth > lastDepth)
                        {
                            lastDepth = currentDepth;
                            lastNodeName = xReader.Name;
                            elementNames.Push(lastNodeName);
                            string xPath = this.GetXPath(elementNames);
                            if (!allPaths.Contains(xPath))
                                allPaths.Add(xPath);
                        }
                        else if (currentDepth == lastDepth)
                        {
                            if (xReader.Name == lastNodeName)
                                continue;
                            else
                            {
                                lastNodeName = xReader.Name;
                                if (elementNames.Count > 0)
                                    elementNames.Pop();
                                elementNames.Push(lastNodeName);
                                string xPath = this.GetXPath(elementNames);
                                if (!allPaths.Contains(xPath))
                                    allPaths.Add(xPath);
                            }
                        }
                        else
                        {
                            lastDepth = xReader.Depth;
                            lastNodeName = xReader.Name;
                            while (elementNames.Count > xReader.Depth)
                                elementNames.Pop();
                            elementNames.Push(lastNodeName);
                            string xPath = this.GetXPath(elementNames);
                            if (!allPaths.Contains(xPath))
                                allPaths.Add(xPath);
                        }
                    }
                    else if (xReader.NodeType == XmlNodeType.EndElement)
                    {

                    }
                }
                xReader.Close();
                foreach (string xPath in allPaths)
                {
                    this.cboWorksheets.Items.Add(xPath);
                }
            }
        }

        private string GetXPath(Stack<string> elementNames)
        {
            string[] elementNamesCopy = elementNames.ToArray();
            string path = string.Empty;
            for (int i = elementNamesCopy.Length - 1; i >= 0; i--)
            {
                if (path.Length > 0)
                    path += "/";
                path += elementNamesCopy[i];
            }
            return path;
        }

        private void PopulateWorksheets(List<string> sheetNames)
        {
            this.cboWorksheets.Items.Clear();
            foreach(string sheetName in sheetNames)
            {
                this.cboWorksheets.Items.Add(sheetName);
            }
        }

        private void cboWorksheets_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if(!this.IsInitialized)
                return;
            if(this.cboWorksheets.SelectedIndex<0)
                return;

            EntityType entityType = (EntityType)this.cboEntityType.SelectedIndex;
            if(this.rdDB.IsChecked.Value)
            {
                TableColumn[] cols = DbUtil.GetTableColumns(
                    DataProviderType.MSSQL, this.txtFilePath.Text, string.Empty, this.cboWorksheets.Text);
                if(cols==null || cols.Length==0)
                    return;
                List<string> colNames=new List<string>();
                foreach(TableColumn col in cols)
                {
                    colNames.Add(col.ColumnName);
                }
                this.UpdateFieldMappingGrid(entityType, colNames);
            }
            else if(this.rdExcel.IsChecked.Value)
            {
                if (!string.IsNullOrEmpty(this.txtFilePath.Text) &&
                this.cboWorksheets.SelectedIndex >= 0 &&
                this.cboEntityType.SelectedIndex >= 0)
                {
                    DataTable dt = ExcelReader.ReadTable(this.txtFilePath.Text, this.cboWorksheets.SelectedItem.ToString());
                    if(dt==null || dt.Columns.Count==0)
                        return;
                    List<string> colNames = new List<string>();
                    foreach (DataColumn col in dt.Columns)
                    {
                        colNames.Add(col.ColumnName);
                    }
                    this.UpdateFieldMappingGrid(entityType, colNames);
                }
            }
            else if(this.rdXml.IsChecked.Value)
            {
                XmlDocument xDoc = new XmlDocument();
                xDoc.Load(this.txtFilePath.Text);
                XmlNodeList xNodes = xDoc.SelectNodes(this.cboWorksheets.SelectedItem.ToString());
                if (xNodes != null && xNodes.Count > 0)
                {
                    List<string> attrNames=new List<string>();
                    XmlNode xNode = xNodes[0];
                    foreach (XmlAttribute attr in xNode.Attributes)
                    {
                        attrNames.Add(attr.Name);
                    }
                    this.UpdateFieldMappingGrid(entityType, attrNames);
                }
            }
            
        }

        private void UpdateFieldMappingGrid(EntityType entityType, List<string> colNames)
        {
            Type type = EntityTypeUtil.GetType(entityType);
            
            PropertyInfo[] props = type.GetProperties();
            this.dvBindings.Rows.Clear();
            foreach (PropertyInfo prop in props)
            {
                if(!prop.CanWrite || !prop.CanRead)
                    continue;

                DataGridViewRow dr = new DataGridViewRow();
                DataGridViewTextBoxCell cellPropName = new DataGridViewTextBoxCell();
                cellPropName.Value = prop.Name;
                dr.Cells.Add(cellPropName);
                DataGridViewComboBoxCell cellSrcField = new DataGridViewComboBoxCell();
                foreach (string colName in colNames)
                {
                    cellSrcField.Items.Add(colName);
                }
                dr.Cells.Add(cellSrcField);
                this.dvBindings.Rows.Add(dr);
            }
            this.dvBindings.Refresh();
        }

        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            if (this.Cancel != null)
                this.Cancel();
            if(this.Parent is Window)
            {
                Window parent = this.Parent as Window;
                parent.Close();
            }
        }

        private void btnOK_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                this._ImportRule.RuleType = RuleType.Import;
                this._ImportRule.RuleName = this.txtRuleName.Text;
                if (this.rdExcel.IsChecked.Value)
                {
                    this._ImportRule.SrcDataFormat = SetDataSourceType.ExcelFile;
                }
                else if (this.rdDB.IsChecked.Value)
                {
                    this._ImportRule.SrcDataFormat = SetDataSourceType.DB;
                }
                else if (this.rdXml.IsChecked.Value)
                {
                    this._ImportRule.SrcDataFormat = SetDataSourceType.XML;
                }

                this._ImportRule.DataSourcePath = this.txtFilePath.Text;
                this._ImportRule.RecordListPath = this.cboWorksheets.Text;
                this._ImportRule.TgtEntityType = (EntityType) Enum.Parse(typeof (EntityType), this.cboEntityType.SelectedItem.ToString());
                this._ImportRule.SrcEntityType = (EntityType)Enum.Parse(typeof(EntityType), this.cboEntityType.SelectedItem.ToString());
                this._ImportRule.EntityFieldSrcFieldMappings = new Dictionary<string, string>();
                foreach (DataGridViewRow dr in this.dvBindings.Rows)
                {
                    if (!dr.IsNewRow && dr.Cells[0].Value != null && dr.Cells[1].Value != null)
                    {
                        string propName = dr.Cells[0].Value.ToString();
                        string srcFieldName = dr.Cells[1].Value.ToString();
                        this._ImportRule.EntityFieldSrcFieldMappings.Add(propName, srcFieldName);
                    }
                }

                if (this.ImportRuleDefined != null)
                    this.ImportRuleDefined(this._ImportRule);

                if (this.Parent is Window)
                {
                    Window parent = this.Parent as Window;
                    parent.Close();
                }
            }
            catch(Exception ex)
            {
                ExceptionHandler.HandlesException(ex);
                MessageBox.Show(ex.Message);
            }
        }

        private void cboEntityType_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (!this.IsInitialized)
                return;

            SetDataSourceType srcType = SetDataSourceType.DB;
            if(rdXml.IsChecked.Value)
                srcType = SetDataSourceType.XML;
            else if(rdExcel.IsChecked.Value)
                srcType = SetDataSourceType.ExcelFile;
            List<string> colNames = new List<string>();

            if (!string.IsNullOrEmpty(this.txtFilePath.Text) &&
                this.cboWorksheets.SelectedIndex >= 0 &&
                this.cboEntityType.SelectedIndex >= 0)
            {
                if (srcType == SetDataSourceType.ExcelFile)
                {
                    DataTable dt = ExcelReader.ReadTable(
                        this.txtFilePath.Text, this.cboWorksheets.SelectedItem.ToString());
                    if (dt == null || dt.Columns.Count == 0)
                        return;
                    foreach (DataColumn col in dt.Columns)
                    {
                        colNames.Add(col.ColumnName);
                    }
                }
                else if(srcType==SetDataSourceType.DB)
                {
                    TableColumn[] cols = DbUtil.GetTableColumns(
                        DataProviderType.MSSQL, this.txtFilePath.Text, string.Empty,
                        this.cboWorksheets.SelectedItem.ToString());
                    if(cols==null || cols.Length==0)
                        return;
                    foreach(TableColumn col in cols)
                    {
                        colNames.Add(col.ColumnName);
                    }
                }
                else
                {
                    XmlDocument xDoc=new XmlDocument();
                    xDoc.Load(this.txtFilePath.Text);
                    XmlNode xNode = xDoc.SelectSingleNode(this.cboWorksheets.SelectedItem.ToString());
                    if(xNode==null || xNode.Attributes ==null || xNode.Attributes.Count==0)
                        return;
                    foreach(XmlAttribute attr in xNode.Attributes)
                        colNames.Add(attr.Name);
                }
                EntityType entityType = (EntityType)this.cboEntityType.SelectedIndex;
                this.UpdateFieldMappingGrid(entityType, colNames);
            }
        }

        #region switch src type
        private void rdExcel_Checked(object sender, RoutedEventArgs e)
        {
            this.SwitchSrcType();
        }

        private void rdDB_Checked(object sender, RoutedEventArgs e)
        {
            this.SwitchSrcType();
        }

        private void rdXml_Checked(object sender, RoutedEventArgs e)
        {
            this.SwitchSrcType();
        }

        private void SwitchSrcType()
        {
            if (this.IsInitialized)
            {
                if (this.rdExcel.IsChecked.Value)
                {
                    this.lblSrcPath.Content = "File path:";
                    this.lblRecordPath.Content = "Worksheet:";
                    this.btnBrowse.Content = "Browse";
                }
                else if (this.rdDB.IsChecked.Value)
                {
                    this.lblSrcPath.Content = "Connection:";
                    this.lblRecordPath.Content = "Table:";
                    this.btnBrowse.Content = "Refresh";
                }
                else if (this.rdXml.IsChecked.Value)
                {
                    this.lblSrcPath.Content = "File path:";
                    this.lblRecordPath.Content = "Path:";
                    this.btnBrowse.Content = "Browse";
                }
            }
        }
        #endregion
    }
}
